﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kugar.Core.BaseStruct;
using Kugar.Core.Configuration;
using Kugar.Core.ExtMethod;
using Kugar.Core.Log;

namespace Kugar.Core.SMSMessage
{
    public class SMSMessageManager : IDisposable
    {
        [ImportMany(typeof(ISMSMessageProvider), RequiredCreationPolicy = CreationPolicy.Shared)]
        private IEnumerable<Lazy<ISMSMessageProvider, Dictionary<string, object>>> _importProvider = null;

        [ImportMany(typeof(ISMSMessageLogProvider))]
        private IEnumerable<Lazy<ISMSMessageLogProvider, Dictionary<string, object>>> _importLogProvider = null;


        private ISMSMessageLogProvider _currentLogProvider = null;

        private ISMSMessageProvider _defaultProvider = null;

        private CompositionContainer _container = null;

        private TimerEx _timer = null;

        private bool _sendLog;
        private bool _receiveLog;

        public SMSMessageManager()
        {
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new DirectoryCatalog(Path.GetDirectoryName(this.GetType().Assembly.Location)));
            //catalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));

            try
            {
                _container = new CompositionContainer(catalog);

                _container.ComposeExportedValue(this);

                _container.ComposeParts(this);

                _sendLog = CustomConfigManager.Default.AppSettings.GetValueByName<string>("SMS_IsLogSendSMS", "false").ToBool();
                _receiveLog = CustomConfigManager.Default.AppSettings.GetValueByName<string>("SMS_IsLogRevSMS", "false").ToBool();

                if ((_sendLog || _receiveLog) && _importLogProvider != null && _importLogProvider.Any())
                {
                    var configProviderName = Configuration.CustomConfigManager.Default.AppSettings.GetValueByName<string>("SMS_LogProvider", "");

                    if (string.IsNullOrWhiteSpace(configProviderName))
                    {
                        _currentLogProvider = _importLogProvider.FirstOrDefault().Value;
                    }
                    else
                    {
                        var temp = _importLogProvider.FirstOrDefault(x => x.Value.Name == configProviderName);

                        if (temp == null)
                        {
                            _currentLogProvider = _importLogProvider.FirstOrDefault().Value;
                        }
                        else
                        {
                            _currentLogProvider = temp.Value;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LoggerManager.CoreLogger.Error(ex.Message);
                throw;
            }

            if (_importProvider == null || !_importProvider.Any())
            {
                return;
            }

            var interval = CustomConfigManager.Default.AppSettings.GetValueByName("SMS_GetSMSInterval").ToInt(5);

            _timer = new TimerEx(getMessage, interval*1000, null);

            _timer.IsStopWhenRun = true;

            //_timer.Start();
        }

        public bool SendMessage(string providerName, string[] mobile, string message)
        {
            int status = 0;

            return SendMessage(providerName, mobile, message, out status);
        }

        public bool SendMessage(string providerName, string[] mobile, string message, out int statusCode)
        {
            if (string.IsNullOrWhiteSpace(providerName))
            {
                throw new ArgumentNullException("providerName");
            }

            if (string.IsNullOrWhiteSpace(message))
            {
                throw new ArgumentNullException("message");
            }

            if (!mobile.HasData())
            {
                throw new ArgumentNullException("mobile");
            }

            var m = mobile.Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();

            if (!m.HasData())
            {
                throw new ArgumentNullException("mobile");
            }

            statusCode = 0;

            var provider = _importProvider.FirstOrDefault(x => x.Value.Name == providerName);

            if (provider == null)
            {
                throw new ArgumentOutOfRangeException("providerName");
            }

            try
            {
                var ret = provider.Value.Send(m, message, out statusCode);

                if (_sendLog && _currentLogProvider != null)
                {
                    foreach (var s in m)
                    {
                        _currentLogProvider.SendLog(providerName.Trim(), s, message, statusCode, ret, "");
                    }
                }

                return ret;
            }
            catch (Exception ex)
            {
                if (_sendLog && _currentLogProvider != null)
                {
                    foreach (var s in m)
                    {
                        _currentLogProvider.SendLog(providerName.Trim(), s, message, statusCode, false, ex.Message);
                    }
                }

                return false;
            }


        }

        public bool SendMessage(string providerName, string mobile, string message)
        {
            int status = 0;

            return SendMessage(providerName, mobile, message, out status);
        }

        public bool SendMessage(string providerName, string mobile, string message, out int statusCode)
        {
            if (string.IsNullOrWhiteSpace(providerName))
            {
                throw new ArgumentNullException("providerName");
            }

            if (string.IsNullOrWhiteSpace(message))
            {
                throw new ArgumentNullException("message");
            }

            if (string.IsNullOrWhiteSpace(mobile))
            {
                throw new ArgumentNullException("mobile");
            }

            statusCode = 0;

            var provider = _importProvider.FirstOrDefault(x => x.Value.Name == providerName);

            if (provider == null)
            {
                throw new ArgumentOutOfRangeException("providerName");
            }

            try
            {
                var ret = provider.Value.Send(new[] { mobile }, message, out statusCode);

                if (_sendLog && _currentLogProvider != null)
                {
                    _currentLogProvider.SendLog(providerName.Trim(), mobile, message, statusCode, ret, "");
                }

                return ret;
            }
            catch (Exception ex)
            {
                if (_sendLog && _currentLogProvider != null)
                {
                    _currentLogProvider.SendLog(providerName.Trim(), mobile, message, statusCode, false, ex.Message);
                }

                return false;
            }


        }

        public void Start()
        {
            _timer.Start();
        }

        public void Stop()
        {
            _timer.Stop();
        }

        public bool SendLog
        {
            set { _sendLog = value; }
            get { return _sendLog; }
        }

        public bool ReceiveLog
        {
            set { _receiveLog = value; }
            get { return _receiveLog; }
        }

        public EventHandler<SmsMessageReceiveEventArgs> SmsMessageReceived;


        private void getMessage(object state)
        {
            Parallel.ForEach(_importProvider, (provider) =>
            {
                SMSMessageItem[] msg = null;

                try
                {
                    msg = provider.Value.GetMessage();
                }
                catch (Exception ex)
                {
                    //Log.LoggerManager.GetLogger("SMSMessageLog").Error(ex.Message);
                    return;
                }


                if (msg != null && msg.Length > 0)
                {
                    if (msg.Length < 100)
                    {
                        foreach (var item in msg)
                        {
                            OnMessageReceive(provider.Value, item);
                        }
                    }
                    else
                    {
                        Parallel.ForEach(msg, (s) =>
                        {
                            OnMessageReceive(provider.Value, s);
                        });
                    }

                }
            });

            foreach (var provider in _importProvider)
            {

            }
        }

        private void OnMessageReceive(ISMSMessageProvider provider, SMSMessageItem item)
        {
            if (SmsMessageReceived != null)
            {
                SmsMessageReceived(this, new SmsMessageReceiveEventArgs(provider.Name, item, this));

                if (_receiveLog && _currentLogProvider != null)
                {
                    _currentLogProvider.ReceiveLog(provider.Name, item.Mobile, item.Message, item.ReceiveDt);
                }
            }
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            foreach (var p in _importProvider)
            {
                p.Value.Dispose();
            }

            foreach (var p in _importLogProvider)
            {
                p.Value.Dispose();
            }
        }

        #endregion


    }

    public class SmsMessageReceiveEventArgs : EventArgs
    {
        public SmsMessageReceiveEventArgs(string providerName, SMSMessageItem messageInfo, SMSMessageManager manager)
        {
            ProviderName = providerName;
            MessageInfo = messageInfo;
            MessageManager = manager;
        }

        public SMSMessageManager MessageManager { get; private set; }
        public string ProviderName { get; private set; }
        public SMSMessageItem MessageInfo { get; private set; }
    }
}
